#include "GameManager.h"

GameManager::GameManager(QObject *parent) :
    QObject(parent) {
}

void GameManager::newGame() {
    for (int i = 0; i < NUM_COLUMNS; i++) {
        columns[i].resetPieces();
    }
    for (int i = 0; i < NUM_WINS; i++) {
        wins[i].length = -1;
    }
    previousMoveIndex = -1;
    currentPlayer = ConnectFour::BLACK;
    isAnimating = false;
    emit playerChanged(currentPlayer);
    emit newGameStarted();
}

void GameManager::requestNewGame() {
    if (!isAnimating) {
        newGameAnimation();
    }
}

void GameManager::requestMove(int index) {
    if (currentPlayer != ConnectFour::NONE && !isAnimating) {
        if (!columns[index].isFull()) {
            columnAnimation(index);
        }
    }
}

void GameManager::requestUndo() {
    if (previousMoveIndex > -1 && !isAnimating) {
        columns[previousMoveIndex].undoPiece();
        emit columnNeedsUpdate(columns + previousMoveIndex, previousMoveIndex);
        previousMoveIndex = -1;
        if (currentPlayer == ConnectFour::RED) {
            currentPlayer = ConnectFour::BLACK;
        } else {
            currentPlayer = ConnectFour::RED;
        }
        emit playerChanged(currentPlayer);
    }
}

void GameManager::columnAnimation(int index) {
    isAnimating = true;
    emit needsAnimation((Animation::AnimationMode) index, columns[index].getNumberOfPieces(), currentPlayer);
}

void GameManager::newGameAnimation() {
    int maxHeight = columns[0].getNumberOfPieces();
    for (int i = 1; i < NUM_COLUMNS; i++) {
        if (maxHeight < columns[i].getNumberOfPieces()) {
            maxHeight = columns[i].getNumberOfPieces();
        }
    }
    isAnimating = true;
    emit needsAnimation(Animation::CLEAR_ALL, maxHeight, ConnectFour::NONE);
}

void GameManager::addPieceToColumn(int index) {
    isAnimating = false;
    columns[index].addPiece(currentPlayer);
    if (currentPlayer == ConnectFour::RED) {
        currentPlayer = ConnectFour::BLACK;
    } else {
        currentPlayer = ConnectFour::RED;
    }
    emit playerChanged(currentPlayer);
    previousMoveIndex = index;
    emit columnNeedsUpdate(columns + index, index);
    checkWinConditions();
    checkDrawConditions();
}

void GameManager::finishAnimation(Animation::AnimationMode mode) {
    switch (mode) {
    case Animation::CLEAR_ALL:
        newGame();
        break;
    default:
        addPieceToColumn((int) mode);
        break;
    }
}

ConnectFour::Piece GameManager::getPiece(int column, int row) {
    return columns[column].getPiece(row);
}

bool GameManager::isAValidPiece(int column, int row) {
    if (column > NUM_COLUMNS - 1 || column < 0 || row > NUM_ROWS - 1 || row < 0) {
        return false;
    }
    return columns[column].getNumberOfPieces() > row;
}

void GameManager::checkDrawConditions() {
    for (int i = 0; i < NUM_COLUMNS; i++) {
        if (!columns[i].isFull()) {
            return;
        }
    }
    currentPlayer = ConnectFour::NONE;
    emit playerChanged(ConnectFour::NONE);
    previousMoveIndex = -1;
}

void GameManager::checkWinConditions() {
    ConnectFour::Piece currentPiece;
    int consec;
    //Check columns
    for (int i = 0; i < NUM_COLUMNS; i++) {
        for (int j = 0; j < columns[i].getNumberOfPieces() - 3; j++) {
            currentPiece = getPiece(i, j);
            for (consec = 0; isAValidPiece(i, j + consec) && currentPiece == getPiece(i, j + consec); consec++) ;
            if (consec > 3) {
                 submitWin(ConnectFour::VERTICAL, i, j, consec);
            }
        }
    }
    //Check rows
    for (int i = 0; i < NUM_COLUMNS - 3; i++) {
        for (int j = 0; j < NUM_ROWS; j++) {
            currentPiece = getPiece(i, j);
            for (consec = 0; isAValidPiece(i + consec, j) && currentPiece == getPiece(i + consec, j); consec++) ;
            if (consec > 3) {
                submitWin(ConnectFour::HORIZONTAL, i, j, consec);
            }
        }
    }
    //Check up-right diagonal
    for (int i = 0; i < NUM_COLUMNS - 3; i++) {
        for (int j = 0; j < NUM_ROWS - 3; j++) {
            currentPiece = getPiece(i, j);
            for (consec = 0; isAValidPiece(i + consec, j + consec) && currentPiece == getPiece(i + consec, j + consec); consec++) ;
            if (consec > 3) {
                submitWin(ConnectFour::UP_RIGHT, i, j, consec);
            }
        }
    }
    //Check up-left diagonal
    for (int i = NUM_COLUMNS - 1; i > 2; i--) {
        for (int j = 0; j < NUM_ROWS; j++) {
            currentPiece = getPiece(i, j);
            for (consec = 0; isAValidPiece(i - consec, j + consec) && currentPiece == getPiece(i - consec, j + consec); consec++) ;
            if (consec > 3) {
                submitWin(ConnectFour::UP_LEFT, i, j, consec);
            }
        }
    }
}

void GameManager::submitWin(ConnectFour::Direction direction, int column, int row, int consecutive) {
    if (wins[0].length < 0 && wins[1].length < 0 && wins[2].length < 0 && wins[3].length < 0) {
            emit winPiece(getPiece(column, row));
            emit playerChanged(ConnectFour::NONE);
    }
    if (wins[direction].length < 0) {
        wins[direction].x = column;
        wins[direction].y = row;
        wins[direction].length = consecutive;
        currentPlayer = ConnectFour::NONE;
        previousMoveIndex = -1;
        emit winNeedsUpdate(wins + direction, direction, getPiece(column, row));
    }
}
